#include "robot.hpp"

#include <math.h>

#include "../config.hpp"

#define PI (3.1415926)

#define DEFAULT_MOV_SPEED (3)
#define DEFAULT_SHOOT_SPEED (30)
#define DEFAILT_HP (1000)
#define DEFAILT_ATTACK_RANGE (200)
#define DEFAILT_DETECT_RANGE (400)
#define DEFAILT_DAMAGE_RANGE (25)

Robot::Robot(Config_Robot_Name_t name, Config_Color_t color) {
  param.Name = name;
  status.angle = 0;

  int x, tmp;
  if (color == CONFIG_COLOR_BLUE) {
    x = SCREEN_WIDTH * 0.9;
    tmp = -200;
  } else {
    x = SCREEN_WIDTH * 0.1;
    tmp = 200;
  }

  switch (name) {
    case Base:
      Place(x, SCREEN_HEIGHT * 0.5);
      param.HP_MAX = 1000;
      param.Attack_Range = 0;
      param.Detection_Range = 0;
      param.Damage = 1;
      param.Speed = DEFAULT_MOV_SPEED;
      param.Attack_Speed = DEFAULT_SHOOT_SPEED;
      break;
    case Hero:
      Place(x, SCREEN_HEIGHT * 0.3);
      param.HP_MAX = DEFAILT_HP * 1.5;
      param.Attack_Range = DEFAILT_ATTACK_RANGE * 1.5;
      param.Detection_Range = DEFAILT_DETECT_RANGE * 1.5;
      param.Damage = DEFAILT_DAMAGE_RANGE * 2;
      param.Speed = DEFAULT_MOV_SPEED * 0.6;
      param.Attack_Speed = DEFAULT_SHOOT_SPEED * 2;
      break;
    case Infantry:
      Place(x, SCREEN_HEIGHT * 0.8);
      param.HP_MAX = DEFAILT_HP * 0.8;
      param.Attack_Range = DEFAILT_ATTACK_RANGE * 1;
      param.Detection_Range = DEFAILT_DETECT_RANGE * 1;
      param.Damage = DEFAILT_DAMAGE_RANGE * 1;
      param.Speed = DEFAULT_MOV_SPEED * 1;
      param.Attack_Speed = DEFAULT_SHOOT_SPEED * 1;
      break;
    case Sentry:
      Place(x + tmp, SCREEN_HEIGHT * 0.5);
      param.HP_MAX = DEFAILT_HP;
      param.Attack_Range = DEFAILT_ATTACK_RANGE * 1.6;
      param.Detection_Range = DEFAILT_DETECT_RANGE * 1.6;
      param.Damage = DEFAILT_DAMAGE_RANGE;
      param.Speed = DEFAULT_MOV_SPEED;
      param.Attack_Speed = DEFAULT_SHOOT_SPEED;
      break;
    default:
      break;
  }
  status.HP = param.HP_MAX;
  status.Attack_heat = 0;
}
void Robot::Hited(float damage) {
  status.HP -= damage;
  if (status.HP < 0) status.HP = 0;
}

void Robot::Hit(Robot* robot) {
  if (status.Attack_heat < param.Attack_Speed)
    status.Attack_heat++;
  else {
    robot->Hited(param.Damage);
    Start(&(status.address), &(robot->status.address));
    status.Attack_heat = 0;
  }
}

void Robot::Place(float x, float y) {
  status.target.x = status.address.x = x;
  status.target.y = status.address.y = y;
}

Robot_ScanAns_t Robot::Scan(Robot* target) {
  float a, b, c, angle_xy;
  a = (target->status.address.x - status.address.x) *
      (target->status.address.x - status.address.x);
  b = (target->status.address.y - status.address.y) *
      (target->status.address.y - status.address.y);
  c = sqrt(a + b);
  angle_xy = atan2f((target->status.address.x - status.address.x),
                    (target->status.address.y - status.address.y)) -
             PI / 2;
  if (c <= param.Attack_Range) {
    status.angle = angle_xy;
    return Scan_Hit;
  }
  if (param.Detection_Range >= c) {
    status.angle = angle_xy;
    return Scan_Detect;
  }
  return Scan_Not_Found;
}

float Robot::Distance(Robot* robot) {
  return sqrt((status.address.x - robot->status.address.x) *
                  (status.address.x - robot->status.address.x) +
              (status.address.y - robot->status.address.y) *
                  (status.address.y - robot->status.address.y));
}

void Robot::SetTarget(float x, float y) {
  status.target.x = x;
  status.target.y = y;
}

void Robot::ChangeTarget(float x, float y) {
  status.target.x += x;
  status.target.y += y;
  if (status.target.x < 0) status.target.x = 0;
  if (status.target.y < 0) status.target.y = 0;
  if (status.target.x > SCREEN_WIDTH) status.target.x = SCREEN_WIDTH;
  if (status.target.y > SCREEN_HEIGHT) status.target.y = SCREEN_HEIGHT;
}

void Robot::Move() {
  float error_x = status.target.x - status.address.x;
  float error_y = status.target.y - status.address.y;

  float _sin =
      sqrt(error_y * error_y / (error_x * error_x + error_y * error_y));
  float _cos =
      sqrt(error_x * error_x / (error_x * error_x + error_y * error_y));
  if ((error_x * error_x + error_y * error_y) >= param.Speed * param.Speed) {
    if (error_y > 0)
      status.address.y += _sin * param.Speed;
    else
      status.address.y -= _sin * param.Speed;
    if (error_x > 0)
      status.address.x += _cos * param.Speed;
    else
      status.address.x -= _cos * param.Speed;
  } else {
    status.address.x = status.target.x;
    status.address.y = status.target.y;
  }
}

Config_Robot_Name_t Robot::Name() { return param.Name; }

Address_t* Robot::Address() { return &(status.address); }

Address_t* Robot::Target() { return &(status.target); }

float Robot::Angle() { return status.angle; }

float Robot::HP() { return status.HP / param.HP_MAX; }

bool Robot::Life() { return status.HP > 0; }
